home *** CD-ROM | disk | FTP | other *** search
/ Technotools / Technotools (Chestnut CD-ROM)(1993).ISO / lang_c / mscheap2 / heapinfo.txt < prev    next >
Text File  |  1990-03-13  |  54KB  |  1,261 lines

  1.  
  2.  
  3.                   Replacement Heap Manager for MSC
  4.                   --------------------------------
  5.  
  6.         Copyright (c) 1990 by Optimal Software, All Rights Reserved
  7.  
  8.             Optimal Software, 4 Lacy Lane, Nashua, NH 03061-2151
  9.  
  10.                    603-880-9844              CIS [73710,406]
  11.  
  12.  
  13.         This document describes a replacement heap manager for MSC 5.1.
  14.         The library is binary compatible with the original MSC functions
  15.         so recompilation is not necessary.  All of the new functions are
  16.         exact replacements for the original MSC "far" heap functions.
  17.  
  18.  
  19. I. General
  20.  
  21.    The replacement heap manager differs from the original MSC heap
  22.    manager in a number of ways.  The following sections address each
  23.    of the differences according to the following categories:
  24.    
  25.        - Section II   : functionality and performance
  26.  
  27.        - Section III  : flexibility and user control
  28.  
  29.        - Sectiom IV   : error handling and debugging
  30.  
  31.        - Section V    : compatibility with MSC run-time library
  32.  
  33.        - Section VI   : version notes
  34.  
  35.        - Section VII  : licensing and distribution
  36.  
  37.  
  38.    There is also a difference in the approach to the design of the
  39.    software.  The design of the MSC heap manager seems to be based on
  40.    a philosophy of procrastination.  I.e., processing is deferred until
  41.    it is absolutely necessary.  This approach enhances the performance
  42.    of basic operations but degrades the overall performance of the
  43.    system. (Penny-wise and pound-foolish).
  44.  
  45.    In contrast, the replacement heap manager is a philosophical
  46.    activist.  I.e., processing is performed as soon as convenient.
  47.    This alternate approach uses more sophisticated basic operations in
  48.    an attempt to maintain the system in the best condition possible.
  49.    (Penny-foolish but pound-wise).
  50.  
  51.    In practice the MSC heap manager may avoid some work that the
  52.    replacement heap manager undertakes.  On the other hand, the
  53.    MSC heap manager is often confronted with pathological situations
  54.    of its own construction.  The replacement heap manager attempts to
  55.    avoid such unpleasantness.
  56.  
  57.    In fact the objectives of the two designs are more easily decribed
  58.    by what they avoid than what they pursue.  The MSC heap manager
  59.    avoids work while the replacement heap manager avoids failure.
  60.  
  61.    If you have a heap-intensive application that does not use much
  62.    memory and maximum performance is worth an occasional crash
  63.    "out of heap memory" stick with the original MSC heap manager.
  64.    It is heavily optimized toward small applications.  If you need
  65.    to manage large heaps, or are tight on memory, or need reliable
  66.    performance, then the replacement heap manager may be useful.
  67.  
  68.  
  69. II. Functionality and performance
  70.  
  71.    These differences are fundamental to the value of the replacement
  72.    heap manager.  They define how well the heap manager does its
  73.    primary job: managing the heap.
  74.  
  75.  
  76.    A. Efficient use of memory
  77.  
  78.       The replacement heap manager does not inadvertently fragment the
  79.       heap in as many ways as the MSC heap manager.  It allows the
  80.       user program to define the direction in which the heap should
  81.       be optimized.
  82.  
  83.       In order to expedite allocation operations there is a global
  84.       list of free chunks.  The list is doubly linked, and ordered
  85.       as defined by _heapmode.  MSC does not appear to have a free
  86.       list.  It seems to rely on the hope that garbage collection is
  87.       infrequent to compensate for the fact than searching the entire
  88.       heap for free entries is extremely expensive.
  89.  
  90.       The replacement heap manager optimizes its operations based on
  91.       the value of the variable _heapmode.
  92.  
  93.       - The _HEAPTIME setting sacrifices space efficiency for
  94.         performance.  The free list is not maintained in order
  95.         and allocation takes the first entry that fits (LIFO).
  96.         This setting is useful for programs with small amounts
  97.         of volatile data.
  98.  
  99.         This setting also tries to meet allocation needs without
  100.         merging free blocks.  If the allocation fails the free
  101.         list is condensed and another attempt is made.  The MSC
  102.         heap manager operates in a similar manner, fragmenting
  103.         the heap and wasting space in an attempt to save time.
  104.  
  105.       - If _heapmode is _HEAPSIZE the heap manager minimizes the space
  106.         occupied by the heap by maintaining the free list in address
  107.         order (first fit).  This technique tends to keep the in-use
  108.         heap entries in low memory so that _heappack() can release
  109.         unused space back to DOS.  This setting is useful for programs
  110.         which spawn other programs, or use large buffers, or are
  111.         generally tight on memory.
  112.  
  113.       - If _heapmode is _HEAPSPACE the heap manager minimizes the
  114.         wasted heap space by maintaining the free list in order of
  115.         size (best fit).  This technique permits the most effective
  116.         use of the available heap space.  It is useful for programs
  117.         that must handle overflow via disk files.  The efficient
  118.         utilization of heap memory minimizes the need for disk access.
  119.  
  120.       Note that changing the _heapmode on the fly does not
  121.       automatically re-order the free list.  Neither does it cause
  122.       problems.  As the heap is used the free list will lose the
  123.       old ordering and conform to the new optimization.
  124.  
  125.       The default mode is described by _HEAPMODE and may be set
  126.       from the compiler command line when the library is built by
  127.       defining the symbol _HEAPMODE.  E.g., -D_HEAPMODE=_HEAPSIZE.
  128.       The factory setting is _HEAPTIME as this is the closest
  129.       approximation to the original MSC heap manager.
  130.  
  131.  
  132.    B. Default data segment automatically minimized at startup.
  133.  
  134.       The link switch /cp does not affect exec/spawned programs.
  135.       They always get a 64Kb default data segment no matter how
  136.       they are built or adjusted with exemod.  The replacement
  137.       heap manager handles this problem automatically.  In programs
  138.       build with "far" data ("compact", "large", and "huge" models)
  139.       the "near" heap is shrunk to the minimum and the rest of the
  140.       memory is returned to DOS.
  141.  
  142.       Credit for this goes to Compuserve/MSSYS/DL3/alloc.arc/reducedd.c
  143.       by Willard Gersbacher [76117,2611].  It is a neat and clean
  144.       solution to the lack of cooperation between MS-DOS and MS-C.
  145.  
  146.  
  147.    C. Rational handling of NULL pointers and requests for zero bytes
  148.  
  149.       There is confusion regarding requests for zero bytes and
  150.       handling of NULL pointers.  The following discussion attempts
  151.       to clarify the documented behavior of the MSC routines, the
  152.       actual behavior of the MSC routines, and the behavior of the
  153.       replacement routines.
  154.  
  155.       1. _Msize()
  156.  
  157.          The MSC documentation does not address the result of
  158.          _msize(NULL).  The actual result is some kind of core
  159.          constant.
  160.  
  161.          The replacement version of _msize( NULL ) returns zero.
  162.  
  163.  
  164.       2. Malloc()
  165.  
  166.          The MSC documentation states that malloc(0) returns NULL.
  167.          This is not true.  Malloc(0) returns a pointer to a
  168.          zero-length entry.
  169.  
  170.          The replacement is exactly the same.
  171.  
  172.  
  173.       3. Calloc()
  174.  
  175.          The MSC documentation states that calloc(0,n) and calloc(n,0)
  176.          return NULL.  This is not true.  Calloc(0,0) returns a pointer
  177.          to a zero-length entry.
  178.  
  179.          The replacement is exactly the same.
  180.  
  181.  
  182.       4. _Expand()
  183.  
  184.          The MSC documentation does not address the behavior of
  185.          _expand() when called with a NULL pointer or a zero length.
  186.          The actual behavior is detailed below.
  187.  
  188.                _expand(  NULL,   0 ) returns NULL
  189.  
  190.                _expand(  NULL, 100 ) returns NULL
  191.  
  192.                _expand( !NULL,   0 ) returns a pointer to   0 bytes
  193.  
  194.                _expand( !NULL, 100 ) returns a pointer to 100 bytes
  195.  
  196.          The replacement is exactly the same.
  197.  
  198.  
  199.       5. Realloc()
  200.  
  201.          The MSC documentation specifies that realloc( NULL, size )
  202.          functions like malloc( size ) and that realloc( pointer, 0 )
  203.          returns NULL.  This is correct except for the case covered
  204.          by both rules (they conflict).  The actual behavior of
  205.          realloc( NULL, 0 ) is to return a pointer to zero bytes as
  206.          shown below.
  207.  
  208.                realloc(  NULL,   0 ) returns a pointer to   0 bytes
  209.  
  210.                realloc(  NULL, 100 ) returns a pointer to 100 bytes
  211.  
  212.                realloc( !NULL,   0 ) returns NULL
  213.  
  214.                realloc( !NULL, 100 ) returns a pointer to 100 bytes
  215.  
  216.          Because the conflict between the realloc() rules creates
  217.          an inconsistency and because of the problems with the
  218.          malloc() documentation vs behavior the replacement for
  219.          realloc() is rational rather than exact.  The difference is
  220.          that realloc( pointer, 0 ) returns a pointer to zero bytes
  221.          instead of NULL.
  222.  
  223.  
  224.       6. The file stdmsc.c is an easy way to verify the accuracy of
  225.          the information provided here.
  226.  
  227.  
  228.    D. DOS interface
  229.  
  230.       The MSC heap manager's interface to DOS automatically rounds
  231.       _amblksiz up to the next power of two when extending the heap
  232.       memory pool.  This action encourages fragmentation of the heap
  233.       because user requests for memory are not usually oriented around
  234.       the powers of two so there are gaps left in the heap when the
  235.       heap is extended.
  236.  
  237.       The replacement heap manager does not force power-of-two sizing
  238.       on the DOS interface.  It rounds _amblksiz up to the next paragraph
  239.       only.  This approach gives the application program precise control
  240.       over the heap.
  241.  
  242.  
  243.    E. Consistent "far" and "huge" heap handling
  244.  
  245.       The Intel architecture, segmented and non-relocatable, is indeed
  246.       "brain-dead", but this has been obvious for decades.  The modern
  247.       problem has more to do with software that amplifies the deficiency
  248.       rather than dealing with it.
  249.  
  250.  
  251.       1. The MSC heap manager cannot handle "huge" heap entries.
  252.          Instead the application program must use the DOS memory
  253.          manager via halloc() for regions that exceed a segment
  254.          (64Kb).  This requirement imposes three negative conditions
  255.          on application programs.
  256.  
  257.  
  258.          a. DOS is slow.
  259.  
  260.             The DOS memory manager has a lot of overhead.  While
  261.             large blocks of memory are usually not volatile, there is
  262.             no reason to impose an unnecessary performance penalty.
  263.  
  264.  
  265.          b. Lack of functionality
  266.  
  267.             The MSC heap manager includes only halloc() and hfree().
  268.             There is no hrealloc(), nor is there a query function
  269.             like _msize() to determine the size of an allocated region.
  270.  
  271.  
  272.          c. No safe way to deal with both types of heap memory
  273.  
  274.             The distinction between "far" and "huge" heap pointers
  275.             is an onerous one.  It requires application programs
  276.             to decide, depending on the size of the request, which
  277.             function to use to obtain the memory block.  Then the
  278.             application must record the selection in order to properly
  279.             dispose of the memory.  Free() will not handle a huge
  280.             heap pointer, and hfree() will not handle a "far" heap
  281.             pointer.
  282.  
  283.  
  284.       2. The replacement heap manager deals with these problems
  285.          by providing a single handler for both address models.
  286.          There is no need to segregate the "far" and "huge" heap
  287.          entries.  In addition to simplifying things for the user,
  288.          this also cuts down on the heap fragmentation and allows
  289.          complete optimization of heap access via _heapmode.
  290.  
  291.          Unfortunately, maintaining compatibility with the MSC heap
  292.          manager implies that the limitations of that design continue
  293.          to influence the use of the heap.  E.g., in order to precisly
  294.          mimic the MSC heap manager, calloc() will fail to allocate
  295.          an area larger than 64Kb.  For that you need _hcalloc().
  296.  
  297.          This is irritating, but not crippling because _hcalloc() is
  298.          a proper superset of calloc().  You can use it anywhere that
  299.          you can use calloc().  Similarly, _hmsize() is valid anywhere
  300.          that _msize() is valid. (However, these substitutions imply
  301.          data conversions of size_t to long).
  302.  
  303.  
  304.    F. Benchmarks
  305.  
  306.       Like all benchmarks, testing a heap manager empirically is an
  307.       invitation to distortion, but it is worth mentioning.  Depending
  308.       on the pattern of use the MSC heap manager is either twice as
  309.       fast as the replacement or 100 times slower.  Two orders of
  310.       magnitude is a lot of variability!
  311.  
  312.  
  313.       1. Speed
  314.  
  315.          The above comparison uses the replacement heap manager as
  316.          the reference point because its performance is predictable.
  317.          The MSC heap manager is far more sensitive to the pattern
  318.          of access.  Two extreme cases serve to illustrate this fact.
  319.  
  320.          Given a performance test based on malloc(constant) where
  321.          the total heap memory pool is much smaller than the size
  322.          of available memory the MSC heap manager is quite fast.
  323.          The replacement heap manager is only half as fast as the
  324.          original under these conditions.
  325.  
  326.          Equally extreme is a test based on random allocations,
  327.          deallocations, and reallocations of random size, where the
  328.          total demand matches the size of available memory.  In this
  329.          case the MSC heap manager chokes on heap fragmentation and
  330.          garbage collection.  It is up to 200 times slower than the
  331.          first test.  The replacement is a slightly slower than it was
  332.          in the first test.
  333.  
  334.          The extreme tests are indicative of trends, but not definitive.
  335.          The file bench.c implements a mixture of 3 parts malloc(),
  336.          2 parts free(), and 1 part realloc() as representative of
  337.          heap processing.  The sizes of the heap entries are skewed
  338.          toward the smaller chunks as an example of reasonable heap
  339.          usage.  The exact distribution is triangular, with a block
  340.          of size {N:0..32767} appearing with a probability of
  341.          (32768-N) / (32768 * 32769 / 2).
  342.  
  343.          Running the above test with various iteration limits confirms
  344.          that the replacement heap manager performs linearly.  It takes
  345.          twice as long to do twice as much.  The MSC performance is not
  346.          so predictable.  It appears to be a power curve of some sort.
  347.  
  348.          The raw data (in seconds) is as follows:
  349.  
  350.            Iterations      MSC     New
  351.            ----------      ---     ---
  352.                  1000        1       0
  353.                  2000        4       1
  354.                  4000        8       2
  355.                  8000       34       4
  356.                 16000       98       8
  357.                 32000      231      17
  358.                 64000      499      33
  359.                128000     1034      66
  360.                256000              132
  361.                512000              265
  362.  
  363.          Incredibly, the above data indicates that the MSC heap
  364.          manager is SLOWER than the replacement heap manager.  Further,
  365.          as the constraints get tighter, the MSC heap manager falls
  366.          further and further behind.
  367.  
  368.          Out of curiosity, we ran the same test using a constant
  369.          quantum of allocation and obtained the following data:
  370.  
  371.                              8 bytes        80 bytes
  372.  
  373.            Iterations      MSC     New     MSC     New
  374.            ----------      ---     ---     ---     ---
  375.                  1000        0       0       0       0
  376.                  2000        0       0       0       0
  377.                  4000        0       0       0       0
  378.                  8000        0       1       0       1
  379.                 16000        0       1       1       1
  380.                 32000        1       3       3       2
  381.                 64000        4       5     125      11
  382.                128000        9      16     395      27
  383.                256000       27      48     919      59
  384.                512000     5128     112    1999     125
  385.  
  386.          It appears that the MSC heap manager is doing fine until
  387.          it has to start garbage collection.  At that point it
  388.          suffers horribly.  Note the sudden transition from about
  389.          1/2 minute to about 3 hours!
  390.  
  391.          All of the above tests were run three times, once for
  392.          each optimization mode.  The size/space optimization modes
  393.          were about 75% as fast as the time-optimized mode.  The
  394.          figures in the tables above reflect the time-optimized
  395.          modes as time optimization is the closest match to the
  396.          functionality of the MSC heap manager.
  397.  
  398.  
  399.       2. Memory
  400.  
  401.          The memory usage statistics can be obtained from the output
  402.          of _fheapdump().  Depending on the usage pattern and the
  403.          optimization mode the memory efficiency varies from 75%
  404.          to 98%.  The space-optimized mode (best fit) turns out to
  405.          be quite efficient.  The size-optimized mode (first fit) is
  406.          almost as efficient, but it does a reasonable job of limiting
  407.          the growth of the heap.  The time-optimized mode (LIFO) is
  408.          roughly 85% as efficient as the space-optimized mode.
  409.  
  410.          Detailed figures were not compiled for the MSC version, but
  411.          inspection of the failure counts in the output of the test
  412.          program showed that even the time-optimized replacement
  413.          heap manager is considerably better than the MSC heap manager.
  414.          This happens because the MSC heap manager wastes more space on
  415.          fragmentation than it saves in overhead per block.
  416.  
  417.  
  418.       3. Real software
  419.  
  420.          As a rough (subjective) test we linked the replacement heap
  421.          manager with a medium sized application (40K LOC, 150Kb
  422.          heap requirement, 450Kb (overlaid) executable) and found
  423.          it noticeably improved.  Previously, it had suffered about
  424.          15% overhead in the form of fragmented, unusable free space.
  425.          With the replacement heap manager the wasted space went to zero.
  426.          Literally there were no free blocks left amoung the allocated
  427.          blocks, just one at the end of the heap.
  428.  
  429.          Try it on one or two of your programs and see what you get.
  430.          You dont have to recompile anything, just link with heap.lib.
  431.  
  432.  
  433. III. Flexibility and user control
  434.  
  435.    The features described here allow the application programmer to
  436.    control the behavior of the heap.  The capability for tuning the
  437.    heap performance simplifies the design of application software
  438.    because it is not necessary to design around the limitations of
  439.    the heap manager.
  440.  
  441.  
  442.    A. Heap entries can be relocated and the heap shrunk as needed.
  443.  
  444.       The _relocate() and _heappack() functions provide an efficient
  445.       method of dealing with multi-phase processing.  _Relocate()
  446.       serves to reposition entries as low as possible in memory.
  447.       Note that your mileage may vary, but _relocate() is guaranteed
  448.       to do no harm.  I.e., it will only act when improvement is
  449.       certain.  Thus it is feasible to make multiple passes through
  450.       allocated memory (typically until nothing moves) in order to
  451.       obtain as much compression as possible.  A pass in order of
  452.       ascending addresses is guaranteed to obtain the maximum possible
  453.       compression in a single pass. 
  454.  
  455.       In addition to compressing the heap in order to maximize the
  456.       free DOS memory, the relocation process is the basis for a
  457.       heap garbage collector.  An allocation that fails due to
  458.       heap fragmentation may succeed if the heap is compacted.
  459.       Applications that cannot afford to crash "out of heap" can
  460.       overcome heap fragmentation by relocating the active heap
  461.       entries.
  462.  
  463.       _heappack() releases excess heap memory back to DOS.
  464.  
  465.       The file packdemo.c illustrates the basic techniques.
  466.  
  467.  
  468.    B. Functional extensions to the MSC design.
  469.  
  470.       These capabilities exceed those of the MSC heap manager, so
  471.       they contain the seeds of incompatibility.  However, their
  472.       use is in no way mandated.  The replacement heap manager
  473.       may be treated exactly as the MSC heap manager.
  474.  
  475.  
  476.       1. Manifest constants
  477.  
  478.          These describe the constants specific to the replacement
  479.          heap manager.
  480.  
  481.          a. SIZE_MAX
  482.  
  483.               This constant describes the maximum size of a heap
  484.               entry.  SIZE_MAX is to size_t as INT_MAX is to int.
  485.               The value is 65535.
  486.  
  487.  
  488.          b. Heap optimization modes
  489.  
  490.             _HEAPTIME, _HEAPSIZE, and _HEAPSPACE define the
  491.             legitimate values for _heapmode.  _HEAPTIME produces
  492.             LIFO free list handling, and maximizes throughput
  493.             at the expense of memory eficiency.  _HEAPSIZE produces
  494.             first-fit free list handling, and minimizes the overall
  495.             size of the heap memory pool.  _HEAPSPACE produces
  496.             best-fit free list handling, and minimizes the wasted
  497.             heap space.
  498.  
  499.             The interpretations are described in detail in section II.A.
  500.  
  501.  
  502.       2. Global variables:
  503.  
  504.          These variables are declared in heap.h and defined in
  505.          heaputil.c.  They provide application programs with control
  506.          over the behavior of the heap manager.
  507.  
  508.          a. int _Heapmode;
  509.  
  510.             _Heapmode controls the optimization mode of the heap
  511.             manager via free list handling algorithms. Section II.A
  512.             describes its use in detail.
  513.  
  514.  
  515.          b. size_t _Heappad;
  516.  
  517.             _Heappad controls the safety margins allocated as part of
  518.             every heap entry.  The default value is zero, but an
  519.             application program may set a non-zero value prior to
  520.             using the heap.
  521.  
  522.             Section IV.B gives details about debugging with _heappad.
  523.  
  524.  
  525.       3. Entry points
  526.  
  527.          These functions extend the power of the heap manager to
  528.          cover previously unaddressed issues.
  529.  
  530.  
  531.          a. void _heapdump( FILE *fp, int verbose );
  532.  
  533.             _heapdump() is a utility function that writes a complete
  534.             description of the state of the heap to the indicated
  535.             stream file.  The report includes the following information:
  536.  
  537.                 - Key to notation
  538.  
  539.                 - Global variables
  540.                    - _heapmode
  541.                    - _heappad
  542.  
  543.                 - Arena headers -- DOS interface
  544.                    - Address [pointer]
  545.                    - Size
  546.                    - Forward and backward links [pointers] on arena list
  547.                    - Chunk headers -- C RTL interface
  548.                       - Address {<pointer>}
  549.                       - Size
  550.                       - Forward and backward links <pointers> on chunk list
  551.                       - Forward and backward links {pointers} on free list
  552.                       - Status: ok, read-only, or bad.
  553.                       - In debug mode additional info is reported:
  554.                          - Function that last changed the entry
  555.                          - Desired size of the entry
  556.                          - Transaction (sequence) number
  557.                          - Name of source file
  558.                          - Line number in source file
  559.  
  560.                 - Controls for free and join lists
  561.                    - Address {pointer}
  562.                    - Number of entries on the list (not including master)
  563.                    - Forward and backward links {pointers}
  564.  
  565.                 - Summary statistics on heap usage
  566.  
  567.                 - Pagination: formfeed
  568.  
  569.             Verbose is a boolean argument that controls the level of
  570.             detail in the heap status report.  In non-verbose mode
  571.             the report excludes the chunk info.
  572.  
  573.  
  574.          b. void  _heappack( void );
  575.  
  576.             _Fheappack() releases unused heap memory back to DOS.  It
  577.             searches the entire heap, shrinking arenas that contain
  578.             active entries to the minimum, and totally freeing arenas
  579.             that contain no active entries.
  580.  
  581.             Section II.B.1 also describes heap compaction.
  582.  
  583.  
  584.          c. int _heapwatch( void far *ptr, int enable );
  585.  
  586.             _Fheapwatch() establishes a heap entry as read-only.  The
  587.             enable argument controls the status of the heap entry.
  588.             _Heapchk() tests all the read-only entries in the heap
  589.             and reports _HEAPBADPTR if one has changed.  Similarly,
  590.             the debugging routines will trigger a diagnostic report
  591.             if a read-only entry changes.
  592.  
  593.             Section IV.F provides details on the use of _fheapwatch().
  594.  
  595.  
  596.          d. char *_heapstat( int status );
  597.  
  598.             _Heapstat() translates heap status codes obtained from
  599.             _heapchk(), _heapset(), and _heapwalk() into descriptive
  600.             strings.
  601.  
  602.  
  603.          e. void far *_relocate( void far *ptr );
  604.  
  605.              _Relocate() repositions a heap entry as low as possible
  606.              in memory.  It is the basis for heap compaction and
  607.              garbage collection as described in section II.B.1.
  608.  
  609.  
  610.          f. "Huge" versions of standard functions
  611.  
  612.             The _hcalloc, _hexpand, _hfree, _hmalloc, _hmsize, and
  613.             _hrealloc functions handle "huge" heap entries (larger
  614.             than 64Kb) just as their '_f' counterparts handle "far"
  615.             heap entries (less than 64Kb).
  616.  
  617.  
  618.       4. Compiler options
  619.  
  620.          These options affect applications that #include "heap.h".
  621.          Enable the special options via the compiler command line
  622.          with -D<symbol>=<value>.
  623.  
  624.  
  625.          a. STDMSC -- enforcing strict source code compatibility
  626.  
  627.             This symbol controls the declaration of the symbols,
  628.             variables, and functions unique to the replacement heap
  629.             manager.  If STDMSC is defined heap.h behaves just like
  630.             malloc.h.  If STDMSC is not defined, the extra capabilites
  631.             of the replacement heap manager are available to
  632.             application programs.
  633.  
  634.  
  635.          b. _HEAPDEBUG -- debugging heap problems
  636.  
  637.             This symbol enables an alternate set of functions that
  638.             diagnose heap problems.  The functions that change the
  639.             heap are replace by equivalents that check the heap
  640.             consistency with _heapchk(), perform the standard heap
  641.             operation, and then record the transaction for diagnostic
  642.             reports triggered by errors.
  643.  
  644.             The defined value of the symbol is taken to be the stream
  645.             on which error reports should appear. For example,
  646.             -D_HEAPDEBUG=stdout sends error reports to the standard
  647.             output stream.
  648.  
  649.             Enabling _HEAPDEBUG also enables a stringent form of pointer
  650.             checking.  Instead of a test on the value of the pointer
  651.             offset, _HEAPDEBUG checks that a matching entry exists in
  652.             the heap (this test is free as _HEAPDEBUG must traverse the
  653.             heap to check for errors anyway).  Thus _HEAPDEBUG guarantees
  654.             that no pointer that happens to have a zero offset can masquerade
  655.             as a heap pointer.
  656.  
  657.             STDMSC overrides _HEAPDEBUG.  If both are defined _HEAPDEBUG
  658.             is ignored.
  659.  
  660.             For further details see section IV.D.
  661.  
  662.  
  663.          c. _HEAPTRACE -- tracing heap activity
  664.  
  665.             This symbol enables an alternate set of functions that
  666.             trace heap activity.  Functions that change the heap are
  667.             replaced by equivalents that write a transaction record
  668.             to the trace log file after every heap access.  The
  669.             transaction reports give the sequence number, the name
  670.             of the function, the value of the parameters, the source
  671.             file and line number and the returned result.
  672.  
  673.             The defined value of the symbol is taken to be the stream
  674.             on which transaction reports should appear.  For example,
  675.             -D_HEAPTRACE=stdout sends reports to the standard output file.
  676.  
  677.             Enabling _HEAPTRACE implies that _HEAPDEBUG is enabled as well.
  678.  
  679.             STDMSC overrides _HEAPTRACE.  If both are defined _HEAPTRACE
  680.             is ignored.
  681.  
  682.             For further details see section IV.E
  683.  
  684.  
  685.    C. Direct access to the heap data structures.
  686.  
  687.       The heap manager is a layer between two interfaces.  On the
  688.       bottom is the DOS memory manager that believes in arenas and
  689.       paragraph alignment, but handles blocks larger than 64 Kb
  690.       easily.  On the top is the C run-time library definition that
  691.       believes in size_t, word alignment, and other limitations of
  692.       32-bit segmented addressing on a 16-bit machine with an 8-bit
  693.       oriented architecture.
  694.  
  695.       The two interfaces are represented explicitly.  On one hand
  696.       there are arenas that match the DOS requirements (typdef _ARENA).
  697.       The memory contained in an arena is managed with a list of chunks
  698.       where each chunk conforms to the requirements of the C interface
  699.       (typedef _CHUNK).
  700.  
  701.       In order to support reallocation and expansion of free'd memory
  702.       blocks there is a separate list of chunks that have been free'd
  703.       that are potential targets for reuse.  Prior to an allocation
  704.       request the entries in this list are added to the free list, and
  705.       adjacent free blocks are joined.  (Thus the name join list from
  706.       to-be-joined).  This list is usualy empty and when not empty it
  707.       is short so the overhead is tiny.
  708.  
  709.       To "walk" the heap you traverse the list of arenas based on
  710.       _heaphead, and within each arena traverse the list of chunks based
  711.       on the arena header.  The free and join lists may also be traveled
  712.       based on their headers _freelist and _joinlist, but this is not
  713.       actually necessary as all chunks, free or used, appear on their
  714.       respective arena-control lists.
  715.  
  716.  
  717.    D. The heap manager will handle non-DOS memory.  If you provide a
  718.       block of memory with an initialized arena and chunk header the
  719.       heap manager will happily manage the it as part of the memory
  720.       pool.  For more information see the get-new-arena portion of
  721.       _xalloc() in heaputil.c.  Be sure to set your _ARENA.isdos to
  722.       FALSE to prevent _heappack() from trying to give it back to DOS.
  723.  
  724.       This feature is especially useful on systems with non-DOS memory
  725.       such as EMS or XMS "upper" memory in the BIOS area from C0000 to
  726.       FFFFF, or video graphics memory in the A0000-AFFFF region.
  727.         
  728.       The next version of the heap manager will provide functions to
  729.       add and delete non-DOS blocks of memory from the heap.
  730.  
  731.  
  732. IV. Error handling and debugging   
  733.  
  734.    The replacement heap manager provides a heap that is harder to damage,
  735.    and easier to debug than the original heap manager.  The following
  736.    sections describes the salient features.
  737.  
  738.  
  739.    A. Comprehensive error checking
  740.  
  741.       The heap checking performed by the MSC _heapchk() is minimal.  It
  742.       appears to be a simple limit test of heap pointers against the
  743.       boundaries of the entire heap area.
  744.  
  745.       The replacement heap manager uses a detailed consistency check.
  746.       Every pointer is validated against the individual arena limits.
  747.       Further, the nodes in the linked lists are checked for reciprocity
  748.       (MSC cannot do this because it does not have a doubly-linked list).
  749.  
  750.       The reciprocity test is an extermely powerful method of heap
  751.       validation.  It is even possible to repair a damaged heap by
  752.       appropriate use of the linked lists.  This facility is not
  753.       included in this library because its usefulness is questionable,
  754.       and it grossly exceeds the fuctionality of the specifications
  755.       (the MSC heap manager).
  756.  
  757.       The consistency check also verifies that the safety margins set
  758.       by _heappad are intact, and that the read-only entries established
  759.       by _heapwatch have not been changed.
  760.  
  761.  
  762.    B. User controlled allocation padding for debugging.
  763.  
  764.       One classic problem with heap management is writing past the end
  765.       of a buffer obtained from the heap.  This typically produces
  766.       extremely hostile effects.  As the routines that handle
  767.       heap-generated buffers are usually embedded in the lower layers
  768.       of software, checking them can be tedious.
  769.  
  770.       One method of testing this kind of problem is to add extra space
  771.       at the ends of the allocated areas.  If the failures stop, there
  772.       is evidence of a heap problem.  Of course, changing sizes moves
  773.       things around which can turn errors on and off randomly so the
  774.       evidence is flimsy.  The problem with this debugging technique
  775.       is that changing the buffer lengths manually is a time-consuming,
  776.       error-prone operation with marginal utility.
  777.  
  778.       The replacement heap manager provides a control for automatically
  779.       extending all heap allocation requests.  Thus an initialization
  780.       parameter is enough to provide evidence for or against heap
  781.       problems.  Typically the padding is set to one or two times the
  782.       size of the structures being manipulated.
  783.  
  784.       Because the padding affects the distance between the data area
  785.       and the heap control structures it cannot be changed after the
  786.       heap has been initialized.  The heap manager takes a copy of
  787.       _heappad when the heap is initialized and uses the copy thereafter.
  788.       Thus _heappad must be set as early as possible, typically in main().
  789.  
  790.       Note that command-line wild cards processed with setargv.obj use
  791.       the heap before main is started.  Normal programs without wild card
  792.       expansion can use:
  793.  
  794.             extern size_t _heappad;     /* declare the global variable */
  795.  
  796.             int main()
  797.             {
  798.             _heappad = 100;             /* set the value */
  799.             }
  800.  
  801.       But programs with wild card handling enabled (those linked with the
  802.       special version of setargv.obj) must use:
  803.  
  804.             size_t _heappad = 100;      /* define the global and its value */
  805.  
  806.             int main()
  807.             {
  808.             }
  809.  
  810.       This sets the value at compile/link time rather than at run time.
  811.  
  812.       The safety margins are automatically filled when a heap entry
  813.       is allocated.  The fill pattern is initially alternating bits,
  814.       0x55, but it is reset by every call to _fheapset().  _Fheapchk()
  815.       compares the two safety margins and reports _HEAPBADPTR if they
  816.       differ.
  817.    
  818.  
  819.    C. Heap pointers are distinctive and are checked for validity.
  820.  
  821.       All heap pointers are aligned on paragraph boundaries, so that
  822.       a non-zero offset indicates an invalid pointer.  Since all of
  823.       the functions receiving pointers as arguments check for invalid
  824.       pointers it is hard to corrupt the heap with a non-heap pointer. 
  825.  
  826.       When an invalid pointer is detected the functions write an error
  827.       messaage to stderr and exit(-1).  To change this behavior adjust
  828.       the _fptr_chk function in heaputil.c
  829.  
  830.       Note that the paragraph alignment principle imples that a
  831.       protected-mode equivalent of the replacement heap manager is
  832.       feasible, with each heap entry having it's own selector value
  833.       and (potentially) a individual set of permissions.
  834.  
  835.  
  836.    D. Debugging with _HEAPDEBUG
  837.  
  838.       The symbol _HEAPDEBUG enables an alternate set of heap functions
  839.       that aid in debugging.  The debug functions keep track of every
  840.       heap entry they process, recording the function name, desired size,
  841.       source file name and line number responsible for the entry, plus a
  842.       transaction (sequence) number.  The debug functions also check the
  843.       heap status prior to every heap access and print a diagnostic report
  844.       if _heapchk() reports a problem.  The value of _HEAPDEBUG is taken
  845.       to be the stream on which diagnostic error reports should appear.
  846.  
  847.       Enabling _HEAPDEBUG also enables a stringent form of pointer
  848.       validity checking.  Instead of a test on the value of the pointer
  849.       offset, _HEAPDEBUG checks that a matching entry exists in the heap
  850.       (this comes free because _HEAPDEBUG must traverse the heap anyway
  851.       to check for errors).  Thus _HEAPDEBUG guarantees that no pointer
  852.       that happens to have a zero offset can masquerade as a heap pointer.
  853.  
  854.       When a debug function detects an error it writes a diagnostic
  855.       report to the indicated stream file and exits.  The report includes
  856.       the following information:
  857.  
  858.             - the function detecting the error (function name, source
  859.               file, and source line number)
  860.  
  861.             - the heap information on the offending entry (actual size,
  862.               address, and used/free status)
  863.  
  864.             - the historical information on the entry (desired size,
  865.               creator function, source file name and line number)
  866.  
  867.             - a dump of the heap as described in section III.B.3.a
  868.               including all of the debugging information
  869.  
  870.       Debugging with an appropriate (non-zero) value in _heappad
  871.       increases the chances of detecting a wild pointer or a pointer
  872.       running past an end of a heap entry.
  873.  
  874.       The debugging capability of the replacement heap manager features
  875.       the following benefits:
  876.  
  877.             - Mapping multiple heap entries to each debug record
  878.               permits debugging of very large heaps by minimizing
  879.               the memory overhead of debug mode.
  880.  
  881.             - Variable safety margin (_heappad) allows you to match
  882.               the size of application data structures.
  883.  
  884.             - No source code changes are necessary to activate the
  885.               debugging capability, only recompilation with the
  886.               -D_HEAPDEBUG=<stream> definition.
  887.  
  888.       The file dbugdemo.c illustrates the basic techniques.
  889.  
  890.  
  891.    E. Debugging with _HEAPTRACE
  892.  
  893.       The symbol _HEAPTRACE enables an alternate set of heap functions
  894.       that aid in debugging.  The debug functions report every heap
  895.       transaction, including the sequence number, the name of the
  896.       function, the value of the parameters, the source file and line
  897.       number, and the returned result.  The transaction reports are 
  898.       sent to the stream defined by -D_HEAPTRACE=<stream>.
  899.  
  900.       Note that the heap transaction sequence number is only a rough
  901.       guide to the chronology of events.  First, heap operations in
  902.       modules compiled without _HEAPTRACE or _HEAPDEBUG do not affect
  903.       the sequence number so there may be hidden transactions.  Second,
  904.       when the transaction is saved in a debug record the sequence
  905.       number is stored as well.  Because all heap entries related to
  906.       an individual function call (and of identical size) share a
  907.       debug record they will all appear to have the sequence number
  908.       of the most recent member.
  909.  
  910.       Enabling _HEAPTRACE also enables _HEAPDEBUG and _HEAPCHECK.
  911.  
  912.  
  913.    F. Monitoring heap data
  914.  
  915.       The _heapwatch() function establishes the read-only status of
  916.       heap entries.  Entries marked read-only are tested by the
  917.       _heapchk/set/walk() family of functions, and also tested by
  918.       the debugger when _HEAPDEBUG or _HEAPTRACE are defined.
  919.  
  920.       Read-only heap entries may be modified, and their read-only
  921.       status preserved by re-invoking _heapwatch() on the affected
  922.       heap entry. 
  923.  
  924.  
  925. V. Compatibility with MSC -- only 99.44%
  926.  
  927.    The replacement heap manager is as close as possible to full
  928.    compatibility with the original MSC heap manager, but, inevitably,
  929.    there are a few differences.
  930.  
  931.  
  932.    A. STDMSC
  933.  
  934.       The symbol STDMSC is available if strict source-code compatibility
  935.       with MSC is required.  Applications compiled with -DSTDMSC are
  936.       guaranteed to compile with MSC's malloc.h instead of heap.h.
  937.  
  938.  
  939.    B. Compromises
  940.  
  941.       Functionally, the replacement heap manager was to be identical
  942.       to the original MSC heap manager, but a few compromises intervened.
  943.  
  944.  
  945.       1. The replacement heap manager is not limited to allocations
  946.          of 65516 bytes or less.  It will supply "far" blocks up to
  947.          65535 (actually 65536) bytes and "huge" blocks of up to 1Mb.
  948.          size.  The MSC restriction of 65516 bytes apparently derives
  949.          from MSC's lack of support for large pointers.  It uses 16-bit
  950.          operations on 32-bit pointers, which means that a region based
  951.          at 1234:000E can only extend to 1234:FFFF.  Thus it loses up
  952.          to 14 bytes due to lack of paragraph alignment.  Another six
  953.          bytes is lost to the heap control block associated with each
  954.          heap entry.
  955.  
  956.          The replacement heap manager uses paragraph alignment and does
  957.          not count its heap control blocks against the segment limit.
  958.          Thus it is able to  deliver a full segment of 64 Kb.  (See 
  959.          SIZE_MAX described in section III.B.1).
  960.  
  961.  
  962.       2. The replacement heap manager is larger than the equivalent
  963.          MSC routines. The difference is several KB depending on which
  964.          functions are used.
  965.  
  966.  
  967.    C. Address models
  968.  
  969.       The replacement heap manager handles the "far" heap.  The "near"
  970.       heap is handled by the standard MSC heap manager.  In "small"
  971.       and "medium" programs, which use "near" as the default data
  972.       address model, the functions calloc(), free(), malloc(), _msize(),
  973.       and realloc() use the "near" heap and are not superceeded by the
  974.       replacement heap library. In "compact", "large", and "huge"
  975.       programs, which use "far" as the default data address model,
  976.       the functions calloc(), free(), malloc(), and realloc() use the
  977.       "far" heap and are superceeded by the replacement heap library.
  978.  
  979.       Note that in "far" address model programs the replacement heap
  980.       manager automatically recovers unused space from the "near" heap
  981.       when the "far" heap is first accessed.  This process is described
  982.       in section II.B.
  983.  
  984.  
  985. VI. Versions (in reverse order)
  986.  
  987.  
  988.    6. Released 15-Mar-90
  989.  
  990.       a. Bug fixes
  991.  
  992.          i. Format of _fheapdump()
  993.  
  994.             The format of the _fheapdump report has been adjusted so that
  995.             it makes sense in all situations.  Perviously, some fields were
  996.             misplaced.
  997.  
  998.          ii. Safety margins on _expand'd blocks
  999.  
  1000.             If _heappad is non-zero, and debugging is enabled, and a block
  1001.             is adjusted with _expand or realloc without requiring repositioning
  1002.             the previous version would report a spurious error because the
  1003.             safety margin was not properly adjusted unless the data was moved.
  1004.  
  1005.          iii. Tracing the "near" heap with _HEAPTRACE
  1006.  
  1007.             The tracing routines for the "near" heap were garbled because
  1008.             they had "far" pointer formats.  The formats are now model
  1009.             dependent.
  1010.  
  1011.  
  1012.       b. Changes
  1013.  
  1014.          i. Interaction of _heappad and SIZE_MAX
  1015.  
  1016.             The value of _heappad reduces the size of the maximum allocation
  1017.             supported by the "far" versions of the heap allocation routines.
  1018.             The functions affected are _fcalloc, _fexpand, _fmalloc, _fmsize,
  1019.             _frealloc, and, in programs compiled with "far" data, calloc,
  1020.             _expand, malloc, _msize, and realloc.
  1021.  
  1022.             This adjustment avoids a possible segment wrap-around due to
  1023.             the partial pointer arithmetic used by the MSC compiler.  If
  1024.             you set _heappad to 100 and malloc(65500) the prior versions
  1025.             would grant the request and return a pointer with an offset
  1026.             of 100.  If the application program attempts to access the
  1027.             entire data region it will exceed the capacity of the 16-bit
  1028.             offset, and the pointer will wrap around to the beginning of
  1029.             the segment.
  1030.  
  1031.             Note that this problem cannot be solved by switching to the
  1032.             "huge" address model because the MSC compiler does not support
  1033.             "huge" pointers, only "huge" arrays.  In fact, there is no way
  1034.             to get the compiler to perform pointer arithmetic properly.
  1035.  
  1036.  
  1037.       c. New features        
  1038.  
  1039.          None.
  1040.  
  1041.  
  1042.    5. Released 10-Mar-90
  1043.  
  1044.  
  1045.       a. Bug fixes
  1046.  
  1047.          None.
  1048.  
  1049.  
  1050.       b. Changes
  1051.  
  1052.          i. All heap access functions are available in "near", "far", and
  1053.             "huge" flavors in addition to the default.  The model-specific
  1054.             versions are all prefixed with '_n', '_f', and '_h' respectively.
  1055.             This lets a "small" model program use _frealloc() on the "far"
  1056.             heap, a capability missing from the original library.
  1057.  
  1058.             The original functions that do not follow this convention are
  1059.             still available from the original library (halloc, hfree, _freect,
  1060.             _memmax, and _memavl).
  1061.  
  1062.  
  1063.          ii. All functions are written in C so that users without an
  1064.              assembler may recompile the library at will.
  1065.  
  1066.  
  1067.       c. New features
  1068.  
  1069.          i. The _relocate() function is now available in "near" and "far"
  1070.             models.  The _nrelocate() version uses _nheapwalk() to find
  1071.             relocation targets.  The _frelocate() version uses the free
  1072.             list to find relocation targets.
  1073.  
  1074.  
  1075.          ii. Debugging and tracing now covers the "near" heap as well as
  1076.              the "far" heap.  However, the debugger is currently unable to
  1077.              record historical information on "near" heap entries.  It
  1078.              does detect and report errors however.
  1079.  
  1080.  
  1081.    4. Released 3-Mar-90.
  1082.  
  1083.       a. Bug fixes
  1084.  
  1085.          i. Hfree.asm was defining _hfree() rather than hfree.  The
  1086.             typo has been corrected.
  1087.  
  1088.          ii. _heapwalk() contained a bug related to multiple arenas
  1089.              that has been fixed.
  1090.  
  1091.  
  1092.       b. Changes
  1093.  
  1094.          i. _Fheapset() has a side effect of resetting the default fill
  1095.             value for the safety margins.
  1096.  
  1097.          ii. The heap manager ignores changes to _heappad after heap
  1098.              initialization.  It uses a snapshot taken at init time
  1099.              to avoid variation in the offset from the control structures
  1100.              to the application data regions.
  1101.  
  1102.  
  1103.       c. New features
  1104.  
  1105.          i. The function _heapstat() is available to convert _heapxxx()
  1106.             status codes to appropriate text strings.
  1107.  
  1108.          ii. The compiler switch -D_HEAPDEBUG=<stream> where <stream> is
  1109.              stdout, stderr, or an application error log file enables
  1110.              special debugging routines.  Functions that modify the
  1111.              heap first check the heap for errors.  They also record
  1112.              the transaction for later reference.  When an error is
  1113.              detected a diagnostic report is written to the idicated
  1114.              output file  For more information see section IV.D.
  1115.  
  1116.          iii. The compiler switch -D_HEAPTRACE=<stream> enables heap
  1117.               function tracing to the indicated stream file.  For
  1118.               more information see section IV.E.
  1119.  
  1120.          iv. Heap.h is now a proper replacement for malloc.h.  The library
  1121.              replaces the far heap in all address models (including "small"
  1122.              and "medium").
  1123.  
  1124.  
  1125.    3. Released 15-Feb-90
  1126.  
  1127.  
  1128.       a. Bug fixes
  1129.  
  1130.          i. _Heapwalk() is now fully MSC compatible.
  1131.  
  1132.  
  1133.       b. Changes
  1134.  
  1135.          i. _Relocate() can handle "huge" heap entries.  _Relocate()
  1136.             ignores _heapmode and always positions the entry as low
  1137.             as possible in memory to maximize the effect of _heappack().
  1138.  
  1139.          ii. Both ends of the heap entries get safety margins _heappad
  1140.             bytes long.  The safety margins are automatically filled
  1141.             with a distinctive bit pattern (0x55) when the region is
  1142.             allocated.
  1143.  
  1144.          iii. Chunk headers are now adjacent to the data region.  This
  1145.             reduces the control overhead from 16 to 8 bytes per block,
  1146.             and removes the need for _HEAPALIGN variants.
  1147.  
  1148.  
  1149.       c. New features
  1150.  
  1151.          i. An alternate method of handling invalid pointers was added.
  1152.             Normally,the functions that accept pointer parameters check
  1153.             the pointers for validity and silently ignore erroneous
  1154.             invocations.  Applications compiled with -DPTRCHK use
  1155.             alternate functions which report errors on stderr.  The error
  1156.             reports include name of the offending function, the value of
  1157.             the invalid pointer, and the source file name and line number.
  1158.  
  1159.  
  1160.    2. With _HEAPALIGN set TRUE the unused area of the header paragraph
  1161.       provides an automatic front-end pad like _heappad provides for the
  1162.       other end of the chunk.  However, the length is fixed (6 bytes). 
  1163.       In version 2 _heapset() affects the front-end pad bytes for all
  1164.       chunks as well as the data areas of free chunks.
  1165.  
  1166.       The _fheapxxx() functions were jumping to heapxxx() rather than
  1167.       _heapxxx().  The typo has been corrected
  1168.  
  1169.       Halloc(), hfree(), and _hsize() are now supported.  The pointers
  1170.       from halloc() are compatible with the pointers from malloc().  This
  1171.       similarity removes one of the major problems of the MSC heap
  1172.       manager: pointer disposal.  In a mixed environment containing both
  1173.       far and huge heap entries, the MSC heap manager requires that the
  1174.       application program track the source of each entry so that the
  1175.       proper function may be used to dispose of the entry when processing
  1176.       is complete.  There is no such requirement with the replacement heap
  1177.       manager.  Free() will handle any heap pointer correctly.
  1178.  
  1179.  
  1180.    1. First release
  1181.  
  1182.  
  1183. VII.  Licensing, distribution, and conditions of use
  1184.  
  1185.  
  1186.    A. This software and the associated documentation is copyright (c)
  1187.       1990 Optimal Sofware, All Rights Reserved.
  1188.  
  1189.  
  1190.    B. This software is provided as a service of Optimal Software.
  1191.       There are no royalties or fees associated with its use.  You
  1192.       may distribute programs linked with this software without
  1193.       restriction.
  1194.  
  1195.       At the urging of several users of this software we have defined
  1196.       the following guidelines for those wishing to make financial
  1197.       contributions toward future software development:
  1198.  
  1199.  
  1200.             Individuals may contribute $25, Students may contribute $15
  1201.  
  1202.             Organizations may contribute $25 per program linked with the
  1203.             replacement library, or $100 for an unlimited number of
  1204.             programs.  (This is not a per-copy count.  There may be any
  1205.             number of copies of a single program.)
  1206.  
  1207.             Commercial software vendors may contribute $100 per product
  1208.             linked with the replacement library, or $400 for an unlimited
  1209.             number of products.
  1210.  
  1211.             Commercial vendors of language products (compilers, assemblers,
  1212.             interpreters, and run-time libraries) may use the replacement
  1213.             heap manager in their program products, as described under
  1214.             commercial software vendors, but may not bundle, merge, or
  1215.             package it with any product without a written license from
  1216.             Optimal Software.
  1217.             
  1218.  
  1219.       All contributors will receive updated copies of the replacement
  1220.       heap manager.
  1221.  
  1222.       We suggest that all users utilize the replacement heap manager as
  1223.       a matter of course, without regard for contributions.  If it turns
  1224.       out that it improves the quality or reliability of your programs,
  1225.       or if it saves you time and effort, you may consider making a
  1226.       financial contribution.
  1227.  
  1228.       If you like the replacement heap manager so much that the above
  1229.       guidelines are not appropriate, contact Microsoft (R) and tell them
  1230.       about your requirements for quality (optimal?) software.  The address
  1231.       is: Microsoft, C Product Manager, PO BOX 97017, Redmond, WA 98073-9917.
  1232.  
  1233.       Microsoft is a registered trademark of Microsoft Corporation.
  1234.  
  1235.  
  1236.    C. Source code for the replacement heap manager is available.  Contact
  1237.       Optimal Software directly for more information.  Educational
  1238.       institutions may qualify for a free source license.
  1239.  
  1240.  
  1241.    D. There is a restriction on the sale or distribution-for-profit of
  1242.       this software.  A written license from Optimal Software is required
  1243.       to sell the software in source or object (unlinked) form.
  1244.  
  1245.       No license is required if the software is distributed for free.
  1246.       However, the entire package must be copied, including this
  1247.       documentation.
  1248.  
  1249.  
  1250.    E. Naturally we are interested in suggestions and improvements.  If
  1251.       you have any please let us know.
  1252.  
  1253.       Lee Winter
  1254.       Optimal Software
  1255.       4 Lacy Lane
  1256.       Nashua, NH 03061-2151
  1257.  
  1258.       603-880-9844
  1259.  
  1260.       CIS [73710,406]
  1261.